package com.programmingteam.util;

/**
 * Geo operations with Lat/Lng points
 */
public class LatLng {

	private final double lat;
	private final double lng;

	public LatLng(double lat, double lng) {
		this.lat = lat;
		this.lng = lng;
	}

	public String toString() {
		return "(" + this.lat + ", " + this.lng + ")";
	}

	public double getLat() {
		return lat;
	}

	public double getLng() {
		return lng;
	}

	/**
	 * @param ll
	 *            LatLng to which distance will be calculated
	 * @return distance between this and ll
	 */
	public double distance(LatLng ll) {

		double er = 6366.707;
		double latFrom = Math.toRadians(getLat());
		double latTo = Math.toRadians(ll.getLat());
		double lngFrom = Math.toRadians(getLng());
		double lngTo = Math.toRadians(ll.getLng());

		double d =
				Math.acos(Math.sin(latFrom) * Math.sin(latTo) + Math.cos(latFrom) * Math.cos(latTo)
						* Math.cos(lngTo - lngFrom))
						* er;

		return d;
	}

	/**
	 * @param distance
	 *            between this and returned point
	 * @param bearing
	 *            to returned point
	 * @return point
	 */
	public LatLng distance(double distance, double bearing) {

		double er = 6366.707;
		double brFrom = Math.toRadians(bearing);
		double latFrom = Math.toRadians(getLat());
		double lngFrom = Math.toRadians(getLng());

		double lat2 =
				Math.asin(Math.sin(latFrom) * Math.cos(distance / er) + Math.cos(latFrom) * Math.sin(distance / er)
						* Math.cos(brFrom));
		double lon2 =
				lngFrom
						+ Math.atan2(Math.sin(brFrom) * Math.sin(distance / er) * Math.cos(latFrom),
								Math.cos(distance / er) - Math.sin(latFrom) * Math.sin(lat2));

		return new LatLng(Math.toDegrees(lat2), Math.toDegrees(lon2));
	}
}
